package com.project.springboot.utils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @description 与java反射机制相关工具类
 * @author 朱正磊
 * @date 2017年9月10日 下午7:07:18
 */
public class ReflectUtil {

	/**
	 * 说明：该方法适用于调用无参方法
	 * @description 通过对象以及对象的方法名调用对象的方法
	 * @param object 对象
	 * @param methodName 方法名
	 * @return 如果methodName方法有返回值，那么returnValue为该返回值，否则returnValue为null
	 */
	public static Object invoke(Object object, String methodName) {
		Object returnValue = invoke(object, methodName, null, null);
		return returnValue;
	}

	/**
	 * 强调：该方法相对来讲使用面比较广泛，建议使用该方法。
	 * 说明：被调用的方法如果有返回值那么本方法返回其返回值，否则返回null
	 * 补充：我们一般情况下利用反射机制都是调用别的类的公共方法，很少调用私有方法，因为我们定义私有方法的初衷就是只在本类里面使用。
	 * @description 通过对象以及对象的方法名和参数调用对象的方法
	 * @param object 对象
	 * @param methodName 方法名
	 * @param classes 参数类型数组
	 * @param params 参数值数组
	 * @return 如果methodName方法有返回值，那么returnValue为该返回值，否则returnValue为null
	 */
	public static Object invoke(Object object, String methodName, Class<?>[] classes, Object[] params) {
		try {
			Class<?> clazz = object.getClass();
			//使用getDeclaredMethod得到的方法可以是公共、受保护、默认（包访问）、私有方法，但不包括继承的方法
			Method method = clazz.getDeclaredMethod(methodName, classes);
			Object returnValue = null;
			if (method.isAccessible()) {//如果是public修饰的方法（公共方法）
				returnValue = method.invoke(object, params);
			} else {//如果是private修饰的方法（私有方法）
				method.setAccessible(true);
				returnValue = method.invoke(object, params);
				method.setAccessible(false);
			}
			return returnValue;
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("程序运行异常！");
		}
	}

	/**
	 * 注意：该方法不建议使用
	 * 强调：使用该方法需要注意，用该方法获取javaBean的属性值即调用getter方法为null，一般用来调用其他方法。
	 * 说明：被调用的方法如果有返回值那么本方法返回其返回值，否则返回null
	 * 补充：我们一般情况下利用反射机制都是调用别的类的公共方法，很少调用私有方法，因为我们定义私有方法的初衷就是只在本类里面使用。
	 * @description 通过对象以及对象的方法名和参数调用对象的方法
	 * @param clazz 类对象
	 * @param methodName 方法名
	 * @param classes 参数类型数组
	 * @param params 参数值数组
	 * @return 如果methodName方法有返回值，那么returnValue为该返回值，否则returnValue为null
	 */
	public static <T> Object invoke(Class<T> clazz, String methodName, Class<?>[] classes, Object[] params) {
		try {
			T t = clazz.newInstance();
			//使用getDeclaredMethod得到的方法可以是公共、受保护、默认（包访问）、私有方法，但不包括继承的方法
			Method method = clazz.getDeclaredMethod(methodName, classes);
			Object returnValue = null;
			if (method.isAccessible()) {//如果是public修饰的方法（公共方法）
				returnValue = method.invoke(t, params);
			} else {//如果是private修饰的方法（私有方法）
				method.setAccessible(true);
				returnValue = method.invoke(t, params);
				method.setAccessible(false);
			}
			return returnValue;
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("程序运行异常！");
		}
	}

	/**
	 * @description 获取javaBean的属性名对应的属性值
	 * @param javaBean 实体类
	 * @param fieldName 属性名
	 * @return 返回属性值
	 */
	public static Object getValueByFieldName(Object javaBean, String fieldName) {
		Field field = getFieldByFieldName(javaBean, fieldName);
		Object valueGet = null;
		if (field != null) {
			valueGet = getOrSetValue(javaBean, field, null, true);
		}
		return valueGet;
	}

	/**
	 * 设置javaBean的属性名对应的属性值
	 * @param javaBean 实体类
	 * @param fieldName 属性名
	 * @param valueSet 属性值
	 */
	public static void setValueByFieldName(Object javaBean, String fieldName, Object valueSet) {
		Field field = getFieldByFieldName(javaBean, fieldName);
		if (field != null) {
			getOrSetValue(javaBean, field, valueSet, false);
		}
	}

	/**
	 * @description 获取javaBean的属性名对应的属性对象
	 * @param javaBean 实体类
	 * @param fieldName 属性名
	 * @return 返回属性类型
	 */
	private static Field getFieldByFieldName(Object javaBean, String fieldName) {
		for (Class<?> superClass = javaBean.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
			try {
				return superClass.getDeclaredField(fieldName);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return null;
	}

	/**
	 * @description 获取或者设置实体类的属性值
	 * @param javaBean 实体类
	 * @param field 属性
	 * @param valueSet 待设置属性值
	 * @param isGet 如果为true，则表示获取属性值；如果为false，则表示设置属性值。
	 * @return 如果是获取属性值，那么返回值有使用的价值，否则如果是设置属性值那么该返回值没有使用的必要。
	 */
	private static Object getOrSetValue(Object javaBean, Field field, Object valueSet, boolean isGet) {
		Object valueGet = null;
		try {
			if (isGet) {
				if (field.isAccessible()) {
					valueGet = field.get(javaBean);
				} else {
					field.setAccessible(true);
					valueGet = field.get(javaBean);
					field.setAccessible(false);
				}
			} else {
				if (field.isAccessible()) {
					field.set(javaBean, valueSet);
				} else {
					field.setAccessible(true);
					field.set(javaBean, valueSet);
					field.setAccessible(false);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return valueGet;
	}
	
	/**
     * @description 不同实体类相同属性赋值，只复制有get,set方法的属性
     * @param objFrom	用于赋值的实体类
     * @param objTo		需要待赋值的实体类
     * @param key		主键名称
     * @throws Exception
     */
	public static void attrCopy(Object objFrom, Object objTo, String key) throws Exception {
		Class<?> clsFrom = null;
		Class<?> clsTo = null;
		try {
			clsFrom = Class.forName(objFrom.getClass().getName());
			clsTo = Class.forName(objTo.getClass().getName());
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		//获取两个实体类的所有属性    
		Field[] objFromFields = clsFrom.getDeclaredFields();
		Field[] objToFields = clsTo.getDeclaredFields();

		//遍历赋值  
		for (Field formField : objFromFields) {
			//过滤主键  
			if (formField.getName().equals(key)) {
				continue;
			}
			Object value = invokeGetMethod(objFrom, formField.getName());
			//如果是null不赋值  
			if (value != null) {
				for (Field toField : objToFields) {
					//找相同的属性  
					if (formField.getName().equals(toField.getName())) {
						Object[] obj = new Object[1];
						obj[0] = value;
						invokeSetMethod(objTo, toField.getName(), obj);
						break;
					}
				}
			}
		}
	}
      
    /** 
     * @description 执行某个Field的getField方法  
     * @param clazz 类 
     * @param fieldName 类的属性名称 
     * @return  
     */  
	private static Object invokeGetMethod(Object clazz, String fieldName) {
		String methodName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
		Method method = null;
		try {
			method = Class.forName(clazz.getClass().getName()).getDeclaredMethod("get" + methodName);
			return method.invoke(clazz);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "";
	}
      
    /** 
     * @description 执行某个Field的setField方法  
     * @param clazz 类 
     * @param fieldName 类的属性名称 
     * @param args 参数，默认为null 
     * @return  
     */  
	private static Object invokeSetMethod(Object clazz, String fieldName, Object[] args) {
		String methodName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
		Method method = null;
		try {
			Class<?>[] parameterTypes = new Class[1];
			Class<?> c = Class.forName(clazz.getClass().getName());
			Field field = c.getDeclaredField(fieldName);
			parameterTypes[0] = field.getType();
			method = c.getDeclaredMethod("set" + methodName, parameterTypes);
			return method.invoke(clazz, args);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "";
	}
	
}
